//
//  HMXMPPManager.m
//  HMWeChat
//
//  Created by apple on 2017/5/10.
//  Copyright © 2017年 itheima. All rights reserved.
//

#import "HMXMPPManager.h"
#import "XMPPLogging.h"
#import "DDLog.h"
#import "DDTTYLogger.h"

static HMXMPPManager * _instance;

@interface HMXMPPManager ()<XMPPStreamDelegate, XMPPAutoPingDelegate, XMPPReconnectDelegate, XMPPRosterDelegate, XMPPIncomingFileTransferDelegate>

//密码
@property (nonatomic, copy) NSString *password;
//注册账号
@property (nonatomic, assign) BOOL registeAccount;
//心跳检测模块
@property (nonatomic, strong) XMPPAutoPing *xmppAutoping;
//自动重连模块
@property (nonatomic, strong) XMPPReconnect *xmppReconnect;
//消息归档模块
@property (nonatomic, strong) XMPPMessageArchiving *xmppMsgArchiving;
//文件接收模块
@property (nonatomic, strong) XMPPIncomingFileTransfer *xmppIncomingFT;
//文件发起者缓存
@property (nonatomic, strong) NSMutableDictionary *fromCache;


@end

@implementation HMXMPPManager

+ (instancetype)sharedManager{

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [HMXMPPManager new];
        //设置日志
        [_instance setupLog];
        //设置模块
        [_instance setupModules];
    });
    return _instance;
}

//设置模块
- (void)setupModules{
    /**
     * 使用模块的一般步骤: 1.创建模块 2.设置属性/代理  3.激活模块
     */
    
    /**
     * 心跳检测
     */
    //设置属性
    //设置心跳包间隔
    self.xmppAutoping.pingInterval = 5;
    //设置心跳超时时长
    self.xmppAutoping.pingTimeout = 5;
    //设置是否响应对端发送来的心跳包
    self.xmppAutoping.respondsToQueries = YES;
    //激活模块
//    [self.xmppAutoping activate:self.xmppStream];
    
    
    /**
     * 自动重连模块
     */
//    [self.xmppReconnect activate:self.xmppStream];
    
    /**
     * 花名册模块
     */
    //设置自动获取服务端保存的联系人列表(xmppframework默认设置应用的生命过程中只会完整的获取一次联系人列表,也可以手动获取通讯录,但是只会执行一次完整的联系人列表同步)
    self.xmppRoster.autoFetchRoster = YES;
    //设置当连接中断时,是否清空内存中记录的联系人及其在线设备数据
//    self.xmppRoster.autoClearAllUsersAndResources = YES;
    //设置是否自动接受好友请求
    self.xmppRoster.autoAcceptKnownPresenceSubscriptionRequests = NO;
    //设置是否支持花名册轻量级操作(一旦设置为YES,就可以在没有完全同步完服务端好友列表的情况下,根据收到的presence节生成联系人对象)
//    self.xmppRoster.allowRosterlessOperation = NO;
    //设置自动清空本地数据
    [XMPPRosterCoreDataStorage sharedInstance].autoRemovePreviousDatabaseFile = NO;
    [self.xmppRoster activate:self.xmppStream];
    
    /**
     * 消息归档模块
     */
    //设置是否同步服务端保存的消息记录,如果设置为NO,就会同步服务端的消息记录
    self.xmppMsgArchiving.clientSideMessageArchivingOnly = YES;
    //设置不清空本地数据
    [XMPPMessageArchivingCoreDataStorage sharedInstance].autoRemovePreviousDatabaseFile = NO;
    [self.xmppMsgArchiving activate:self.xmppStream];
    
    /**
     * 电子名片模块
     */
    [self.xmppvCard activate:self.xmppStream];
    /**
     * 头像模块
     */
    [self.xmppAvatar activate:self.xmppStream];
    
    /**
     * 文件接收模块
     */
    //设置是否自动接收文件
    self.xmppIncomingFT.autoAcceptFileTransfers = NO;
    [self.xmppIncomingFT activate:self.xmppStream];
    
    
    /**
     * 文件发送模块
     */
    [self.xmppOutgoingTF activate:self.xmppStream];
}

//设置xml日志
- (void)setupLog{
    //设置xml数据的打印日志
    [DDLog addLogger:[DDTTYLogger sharedInstance] withLogLevel:XMPP_LOG_FLAG_SEND_RECV];
}

- (void)loginWithJID:(XMPPJID *)jid andPassword:(NSString *)password{
    //连接服务器
    [self connectWithJID:jid andPassword:password];
}


- (void)registerWithJID:(XMPPJID *)jid andPassword:(NSString *)password{
    //设置标记
    self.registeAccount = YES;
    //连接服务器
    [self connectWithJID:jid andPassword:password];
}

- (void)connectWithJID:(XMPPJID *)jid andPassword:(NSString *)password{

    //设置ip地址&端口号&jid
    self.xmppStream.hostName = @"127.0.0.1";
    self.xmppStream.hostPort = 5222;
    self.xmppStream.myJID = jid;
    self.password = password;
    //建立连接
    [self.xmppStream connectWithTimeout:-1 error:nil];
}

#pragma mark - XMPPIncomingFileTransferDelegate

//文件接收失败后调用
- (void)xmppIncomingFileTransfer:(XMPPIncomingFileTransfer *)sender didFailWithError:(NSError *)error{

    NSLog(@"文件接收失败:%@", error);
}

//接收到文件传输请求后调用
- (void)xmppIncomingFileTransfer:(XMPPIncomingFileTransfer *)sender didReceiveSIOffer:(XMPPIQ *)offer{

    NSLog(@"接收到文件传输请求:%@", offer);
    
    //同意接收文件
    [sender acceptSIOffer:offer];
    
    //获取文件发起者(from属性的值)
    NSString *from = [offer attributeStringValueForName:@"from"];
    
    //获取文件名称
    DDXMLElement *siElement = offer.children.lastObject;
    for (DDXMLElement *child in siElement.children) {
        
        if ([child.name isEqualToString:@"file"]) {
            
            NSString *name = [child attributeStringValueForName:@"name"];
            
            NSLog(@"%@:%@", from, name);
            //建立文件名称和文件发起者的对应关系  目的是为了保证发起者和收到的文件能够保持对应
            [self.fromCache setValue:from forKey:name];
        }
    }
    
}



/**
 成功接收到文件后调用

 @param sender 模块
 @param data 接收到的数据
 @param name 文件名
 */
- (void)xmppIncomingFileTransfer:(XMPPIncomingFileTransfer *)sender didSucceedWithData:(NSData *)data
                           named:(NSString *)name{

    NSLog(@"接收到文件:%@", name);
    //手动将文件保存到磁盘中
    NSString *path = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES).lastObject stringByAppendingPathComponent:name];
    [data writeToFile:path atomically:YES];
    
    //为了用户体验,需要将收到的文件以消息的形式展示在界面上
    
    //1.将文件伪装成别人给我发的消息 to应该是登录的账号
    XMPPMessage *message = [XMPPMessage messageWithType:@"chat" to:[HMXMPPManager sharedManager].xmppStream.myJID];
    //设置内容
    [message addBody:name];
    //设置消息的发起者 设置from属性的值
    [message addAttributeWithName:@"from" stringValue:self.fromCache[name]];
    //为了更新好展示不同的消息,需要对消息进行分类,并且应该由发送方来设置消息的类型
    //为了测试,在收到消息后,对消息进行类型的设置
    //判断文件的后缀
    NSRange range = [name rangeOfString:@"."];
    HMXMPPMsgType type;
    
    NSString *typeStr = [name substringFromIndex:range.location + 1];
    if ([typeStr isEqualToString:@"jpg"] || [typeStr isEqualToString:@"png"]) { //图片类型
        
        type = HMXMPPMsgTypeIMG;
        
    }else if ([typeStr isEqualToString:@"mp3"]) { //音频类型
    
        type = HMXMPPMsgTypeAUD;
        
    } else { //其他类型
    
        type = HMXMPPMsgTypeFILE;
    }
    //类型关联到message节中
    [message addSubject:[NSString stringWithFormat:@"%lu", type]];
    
    //2.保存到数据库中
    [[XMPPMessageArchivingCoreDataStorage sharedInstance] archiveMessage:message outgoing:NO xmppStream:[HMXMPPManager sharedManager].xmppStream];
    

}


#pragma mark - XMPPRosterDelegate

//已经接收到出席订阅请求后调用
- (void)xmppRoster:(XMPPRoster *)sender didReceivePresenceSubscriptionRequest:(XMPPPresence *)presence{
    
    NSLog(@"接收到出席订阅请求");
    //区分主动添加好友 还是 接受好友请求(无论主动/被动添加好友,都会执行订阅好友信息的操作)
    //如果是我主动添加别人,数据库中该联系人的subscription字段应该为to,如果别人加我,则联系人表中没有该联系人(subscription为nil)
    XMPPUserCoreDataStorageObject *userCDObj = [[XMPPRosterCoreDataStorage sharedInstance] userForJID:presence.from xmppStream:self.xmppStream managedObjectContext:[XMPPRosterCoreDataStorage sharedInstance].mainThreadManagedObjectContext];
    
    if ([userCDObj.subscription isEqualToString:@"to"]) { //我加别人
        
        UIAlertController *alertVc = [UIAlertController alertControllerWithTitle:@"好友通知" message:[NSString stringWithFormat:@"%@已经成为您的好友", presence.from.user] preferredStyle:UIAlertControllerStyleAlert];
        
        //同意对方的订阅
        [self.xmppRoster acceptPresenceSubscriptionRequestFrom:presence.from andAddToRoster:YES];
        
        [[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alertVc animated:YES completion:nil];
        
        //隐藏弹窗
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            
            [alertVc dismissViewControllerAnimated:YES completion:nil];
        });
        
        
    } else { //别人加我
    
        //接收到好友请求后,应该让用户来选择是否接收好友请求
        UIAlertController *alertVc = [UIAlertController alertControllerWithTitle:@"好友请求" message:[NSString stringWithFormat:@"收到来自%@的好友请求", presence.from.user] preferredStyle:UIAlertControllerStyleAlert];
        //添加动作
        UIAlertAction *action1 = [UIAlertAction actionWithTitle:@"同意" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
            
            //接受好友请求
            [self.xmppRoster acceptPresenceSubscriptionRequestFrom:presence.from andAddToRoster:YES];
        }];
        
        UIAlertAction *action2 = [UIAlertAction actionWithTitle:@"拒绝" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
            
            //拒绝好友请求
            [self.xmppRoster rejectPresenceSubscriptionRequestFrom:presence.from];
        }];
        [alertVc addAction:action1];
        [alertVc addAction:action2];
        //展示弹窗
        [[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alertVc animated:YES completion:nil];
    }
    
}


#pragma mark - XMPPReconnectDelegate

//已经检测出异常断开后调用
- (void)xmppReconnect:(XMPPReconnect *)sender didDetectAccidentalDisconnect:(SCNetworkConnectionFlags)connectionFlags{

    
}


/**
 设置是否执行自动重连

 @param sender 模块
 @param connectionFlags 网络请求
 @return 设置是否执行自动重连    systemconfiguration 检测网络情况 c语言类库  AFN集成iOS的系统类库ReachAbility(集成systemconfiguration)来实现网络检测
 */
- (BOOL)xmppReconnect:(XMPPReconnect *)sender shouldAttemptAutoReconnect:(SCNetworkConnectionFlags)connectionFlags{
    
    
    return YES;
}


#pragma mark - XMPPAutoPingDelegate

//已经发送心跳包后调用
- (void)xmppAutoPingDidSendPing:(XMPPAutoPing *)sender{
    
    NSLog(@"发送心跳包");
}

//已经接收到心跳包的响应后调用
- (void)xmppAutoPingDidReceivePong:(XMPPAutoPing *)sender{

    NSLog(@"接收到响应后调用");
}


//心跳包超时后调用
- (void)xmppAutoPingDidTimeout:(XMPPAutoPing *)sender{

    NSLog(@"连接异常");
}


#pragma mark - XMPPStreamDelegate

//客户端连接到服务器后调用
- (void)xmppStreamDidConnect:(XMPPStream *)sender{

    NSLog(@"连接建立");
    //区分登录/注册
    if (self.registeAccount) { //注册
        
        //提交账号信息
        [self.xmppStream registerWithPassword:self.password error:nil];
        
    } else { //登录
    
        //认证密码
        [self.xmppStream authenticateWithPassword:self.password error:nil];
    }
}


- (void)xmppStreamDidRegister:(XMPPStream *)sender{

    NSLog(@"注册成功");
}

//用户登录后调用
- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender{

    NSLog(@"用户登录成功");
    //设置在线状态
//    XMPPPresence *presence = [[XMPPPresence alloc] initWithXMLString:@"<presence type = 'available'><show>dnd</show></presence>" error:nil];
    XMPPPresence *presence = [XMPPPresence presence];

    //设置预制状态(添加show的子元素)
    [presence addChild:[DDXMLNode elementWithName:@"show" stringValue:@"dnd"]];
    //设置自定义状态(QQ的说说) 必须先设置预制状态
    [presence addChild:[DDXMLNode elementWithName:@"status" stringValue:@"最近比较烦~"]];
    //发送presence节给服务器
    [self.xmppStream sendElement:presence];
    
    //跳转控制器
    UIStoryboard *rootSB = [UIStoryboard storyboardWithName:@"Root" bundle:nil];
    UITabBarController *tabBarVc = [rootSB instantiateInitialViewController];
    [UIApplication sharedApplication].keyWindow.rootViewController = tabBarVc;
}

#pragma mark - 懒加载

- (XMPPStream *)xmppStream{
    //创建数据流对象(socket)
    if (_xmppStream == nil) {
        _xmppStream = [[XMPPStream alloc] init];
        //监听socket状态
        [_xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];
    }
    return _xmppStream;
}

- (XMPPAutoPing *)xmppAutoping{
    
    if (_xmppAutoping == nil) {
        
        _xmppAutoping = [[XMPPAutoPing alloc] initWithDispatchQueue:dispatch_queue_create(0, 0)];
        //设置代理 监听心跳包情况
        [_xmppAutoping addDelegate:self delegateQueue:dispatch_get_main_queue()];
    }
    return _xmppAutoping;
}

- (XMPPReconnect *)xmppReconnect{
    if (_xmppReconnect == nil) {
        _xmppReconnect = [[XMPPReconnect alloc] initWithDispatchQueue:dispatch_get_main_queue()];
        //设置代理 监听网络请求,设置是否进行自动重连
        [_xmppReconnect addDelegate:self delegateQueue:dispatch_get_main_queue()];
    }
    return _xmppReconnect;
}

- (XMPPRoster *)xmppRoster{
    if (_xmppRoster == nil) {
        
        _xmppRoster = [[XMPPRoster alloc] initWithRosterStorage:[XMPPRosterCoreDataStorage sharedInstance] dispatchQueue:dispatch_get_main_queue()];
        //设置代理 监听好友请求
        [_xmppRoster addDelegate:self delegateQueue:dispatch_get_main_queue()];
    }
    return _xmppRoster;
}

- (XMPPMessageArchiving *)xmppMsgArchiving{
    
    if (_xmppMsgArchiving == nil) {
        
        _xmppMsgArchiving = [[XMPPMessageArchiving alloc] initWithMessageArchivingStorage:[XMPPMessageArchivingCoreDataStorage sharedInstance] dispatchQueue:dispatch_get_main_queue()];
    }
    return _xmppMsgArchiving;
}

- (XMPPvCardTempModule *)xmppvCard{
    if (_xmppvCard == nil) {
        _xmppvCard = [[XMPPvCardTempModule alloc] initWithvCardStorage:[XMPPvCardCoreDataStorage sharedInstance] dispatchQueue:dispatch_get_main_queue()];
    }
    return _xmppvCard;
}

- (XMPPvCardAvatarModule *)xmppAvatar{
    if (_xmppAvatar == nil) {
        _xmppAvatar = [[XMPPvCardAvatarModule alloc] initWithvCardTempModule:self.xmppvCard dispatchQueue:dispatch_get_main_queue()];
    }
    return _xmppAvatar;
}

- (XMPPIncomingFileTransfer *)xmppIncomingFT{

    if (_xmppIncomingFT == nil) {
        _xmppIncomingFT = [[XMPPIncomingFileTransfer alloc] initWithDispatchQueue:dispatch_get_global_queue(0, 0)];
        //设置代理 监听文件接收情况
        [_xmppIncomingFT addDelegate:self delegateQueue:dispatch_get_global_queue(0, 0)];
    }
    return _xmppIncomingFT;
}

- (NSMutableDictionary *)fromCache{
    if (_fromCache == nil) {
        _fromCache = [[NSMutableDictionary alloc] init];
    }
    return _fromCache;
}

- (XMPPOutgoingFileTransfer *)xmppOutgoingTF{
    if (_xmppOutgoingTF == nil) {
        _xmppOutgoingTF = [[XMPPOutgoingFileTransfer alloc] initWithDispatchQueue:dispatch_get_global_queue(0, 0)];
    }
    return _xmppOutgoingTF;
}

@end
